package com.abl.core.service.sys.impl;


import com.abl.core.common.cache.Cache;
import com.abl.core.common.cache.CacheKey;
import com.abl.core.common.security.AuthUser;
import com.abl.core.common.util.AuthKit;
import com.abl.core.common.util.IdKit;
import com.abl.core.common.util.JwtKit;
import com.abl.core.common.util.StringKit;
import com.abl.core.common.util.password.PWD;
import com.abl.core.domain.db.*;
import com.abl.core.domain.project.MSG;
import com.abl.core.domain.project.PageSearch;
import com.abl.core.domain.project.ServiceException;
import com.abl.core.domain.project.TokenVO;
import com.abl.core.domain.wx.WxConLoginType;
import com.abl.core.domain.wx.WxtMpLoginParam;
import com.abl.core.mybatis.mapper.*;
import com.abl.core.service.sys.SysDictService;
import com.abl.core.service.sys.SysUserService;
import com.abl.core.service.wx.WxService;
import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.abl.core.domain.db.table.LtConnectTableDef.LT_CONNECT;
import static com.abl.core.domain.db.table.LtMemberTableDef.LT_MEMBER;
import static com.abl.core.domain.db.table.SysUserTableDef.SYS_USER;

/**
 * 用户表实现
 */
@Slf4j
@Service
public class SysUserServiceImpl implements SysUserService {
    @Resource
    private Cache<String> cache;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private SysRoleMapper sysRoleMapper;

    @Resource
    private SysDictService dictService;

    @Resource
    private LkUserRoleMapper userRoleMapper;

    @Resource
    private LtMemberMapper ltMemberMapper;

    @Resource
    private LtConnectMapper ltConnectMapper;

    @Resource
    private WxService wxService;

    //小程序一键登录
    @Override
    public TokenVO wxMpNoPhoneLogin(WxtMpLoginParam params) {
//        if (params.getEncryptedData() == null) throw new ServiceException(MSG.USER_MAST_LOGIN_LT);
//        if (params.getEncryptedData() == null) throw new ServiceException(MSG.USER_MAST_ID_CARD_LT);


        //code 微信返回code 用于与微信交互获取openid 等信息
        String code = params.getCode();
        log.error("【小程序联合登陆】【请求参数】 \n{}", JSONObject.toJSONString(params));

        //开放平台下-微信小程序-联合登陆信息
        JSONObject uniConnect = wxService.getConnectLoginParam(code, WxConLoginType.MP);
        log.error("【小程序联合登陆】【响应信息】 \n{}", uniConnect.toJSONString());
        if (uniConnect.getString("errcode") != null) throw new ServiceException(MSG.USER_CONNECT_ERROR);

        //{"session_key":"AhxKsE2thdnkQ7cPk8Pekg==","openid":"oIgWU655vivyAbHrjeqCLkyIRPCA","unionid":"o2tTO6rOjeUgcG2kmy2RSi7J0DAk"}
        String sessionKey = uniConnect.getString("session_key");
        String unionid = uniConnect.getString("unionid");
        String openId = uniConnect.getString("openid");

        //查询绿添系统:是否存在登录信息
        LtConnect ltConnect = ltConnectMapper.selectOneByQuery(
                QueryWrapper.create()
                        .where(LT_CONNECT.UNION_ID.eq(unionid))
                        .and(LT_CONNECT.UNION_TYPE.eq("WECHAT"))
        );
        if (ltConnect == null) throw new ServiceException(MSG.USER_MAST_LOGIN_LT);

        //检查绿添系统:是否已实名
        LtMember ltMember = ltMemberMapper.selectOneByQuery(
                QueryWrapper.create()
                        .where(LT_MEMBER.ID.eq(ltConnect.getUserId()))
                        .and(LT_MEMBER.CARD.ne("0"))
        );
        if (ltMember == null) throw new ServiceException(MSG.USER_MAST_ID_CARD_LT);

        //本系统是否已经存在用户信息
        SysUser sysUser = sysUserMapper.selectOneByQuery(QueryWrapper.create().where(SYS_USER.LT_ID.eq(ltConnect.getUserId())));
        //不存在，创建用户
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setAccount(ltMember.getUsername());
            sysUser.setNickName(ltMember.getNickName());
            sysUser.setPhoneMobile(ltMember.getMobile());
            sysUser.setAvatar(ltMember.getFace());
            sysUser.setLtId(ltMember.getId());

            //插入会议号
            List<String> hasCodes = sysUserMapper.selectObjectListByQueryAs(QueryWrapper.create().select(SYS_USER.MT_CODE), String.class);
            String randomCode = StringKit.createRandom(10, false, hasCodes);
            sysUser.setMtCode(randomCode);

            sysUserMapper.insertSelective(sysUser);
        }
        //更新用户信息
        else {
            //sysUser.setAccount(ltMember.getUsername());
            //sysUser.setNickName(ltMember.getNickName());
            sysUser.setPhoneMobile(ltMember.getMobile());
            //            sysUser.setAvatar(ltMember.getFace());
            sysUserMapper.update(sysUser);
        }

        return createToken(sysUser);
    }
//
//    @Override
//    public JSONObject selectLtMpInfo(String type) {
//        if ("REGISTER".equals(type)) {
//        }
//        if ("AUTH_CODE".equals(type)) {
//        }
//        return null;
//    }

    @Override
    public TokenVO appLogin(String phoneMobile, String md5Pwd) {
        //检查禁止登录
        String LOGIN_IS_DISABLE = CacheKey.USER_APP_LOGIN_IS_DISABLE + phoneMobile;
        String isDisable = cache.getString(LOGIN_IS_DISABLE);
        if (isDisable != null) throw new ServiceException(MSG.USER_LOGIN_IS_DISABLE);

        //查询用户信息
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.from(LtMember.class);
        wrapper.where(LT_MEMBER.MOBILE.eq(phoneMobile));
        LtMember ltMember = ltMemberMapper.selectOneByQuery(wrapper);

        //系统中不存在此用户信息
        if (ltMember == null) throw new ServiceException(MSG.APP_MAST_REGISTER_TO_LT);

        //密码错误
        if (!PWD.matchMd5Pwd(md5Pwd, ltMember.getPassword())) {
            loginPwdError(LOGIN_IS_DISABLE, phoneMobile, "APP");
            throw new ServiceException(MSG.USER_LOGIN_PASSWORD_ERROR);
        }

        //查询用户信息
        wrapper = new QueryWrapper();
        wrapper.from(SysUser.class);
        wrapper.where(SYS_USER.PHONE_MOBILE.eq(phoneMobile));
        SysUser sysUser = sysUserMapper.selectOneByQuery(wrapper);


        //系统中不存在，直接注册
        if (sysUser == null) {
            sysUser = new SysUser();
            sysUser.setAccount(ltMember.getUsername());
            sysUser.setPassword(ltMember.getPassword());
            sysUser.setNickName(ltMember.getNickName());
            sysUser.setAvatar(ltMember.getFace());
            sysUser.setPhoneMobile(ltMember.getMobile());
            sysUser.setLtId(ltMember.getId());

            //插入会议号
            List<String> hasCodes = sysUserMapper.selectObjectListByQueryAs(QueryWrapper.create().select(SYS_USER.MT_CODE), String.class);
            String randomCode = StringKit.createRandom(10, false, hasCodes);
            sysUser.setMtCode(randomCode);

            sysUserMapper.insertSelective(sysUser);
            //TODO 插入角色信息
        }

        //存在用户，判断状态
        else {
            if ("1".equals(sysUser.getIsDisable())) throw new ServiceException(MSG.USER_IS_DISABLE);
        }

        return createToken(sysUser);
    }

    @Override
    public TokenVO sysLogin(String account, String plainPassword, String type) {
        //检查禁止登录
        String LOGIN_IS_DISABLE = CacheKey.USER_SYS_LOGIN_IS_DISABLE + account;
        String isDisable = cache.getString(LOGIN_IS_DISABLE);
        if (isDisable != null) throw new ServiceException(MSG.USER_LOGIN_IS_DISABLE);

        //查询用户信息
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.select(SYS_USER.IS_DISABLE, SYS_USER.PASSWORD, SYS_USER.ACCOUNT, SYS_USER.ID);
        wrapper.from(SysUser.class);
        wrapper.where(SYS_USER.ACCOUNT.eq(account));
        SysUser userInfo = sysUserMapper.selectOneByQuery(wrapper);

        if (userInfo == null) throw new ServiceException(MSG.USER_LOGIN_IS_NOT_EXISTS);
        if ("1".equals(userInfo.getIsDisable())) throw new ServiceException(MSG.USER_IS_DISABLE);

        //比对密码
        if (!PWD.matchPlainPwd(plainPassword, userInfo.getPassword())) {
            loginPwdError(LOGIN_IS_DISABLE, account, "SYS");
            throw new ServiceException(MSG.USER_LOGIN_PASSWORD_ERROR);
        }

        return createToken(userInfo);
    }

    //记录错误次数并抛出异常
    private void loginPwdError(String LOGIN_IS_DISABLE, String account, String type) {
        //记录1小时内的错误次数
        String COUNT_ERROR_KEY = CacheKey.USER_LOGIN_ERROR_COUNT + account + "_" + type;
        cache.incr(COUNT_ERROR_KEY, 60 * 60L);
        String errorCount = cache.getString(COUNT_ERROR_KEY);

        //超过10次封60分钟
        if (Integer.parseInt(errorCount) >= 10) {
            cache.set(LOGIN_IS_DISABLE, "1", 60 * 60L);//封60分钟
            cache.set(COUNT_ERROR_KEY, "0");//重置错误次数
            log.error("【系统禁止用户登录提醒】\n【用户错误登录，账号冻结报警，已被冻结60分钟】\n 用户={} ; 类型={} ; LOGIN_IS_DISABLE={}", account, type, LOGIN_IS_DISABLE);
        }

    }

    @Override
    public boolean sysUserRegister(String account, String plainPassword, String type) {
        //查询用户信息
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.select(SYS_USER.IS_DISABLE);
        wrapper.from(SysUser.class);
        wrapper.where(SYS_USER.ACCOUNT.eq(account).or(SYS_USER.PHONE_MOBILE.eq(account)).or(SYS_USER.EMAIL.eq(account)));
        SysUser userInfo = sysUserMapper.selectOneByQuery(wrapper);

        if (userInfo != null) throw new ServiceException(MSG.USER_LOGIN_IS_REGISTER);

        String userPwd = PWD.createPlainPwd(plainPassword);

        SysUser sysUser = new SysUser();
        sysUser.setPassword(userPwd);
        sysUser.setAccount(account);
        sysUser.setNickName("abl" + account);

        int insert1 = sysUserMapper.insertSelective(sysUser);

        return true;
    }

//
//    @Override
//    public TokenVO appLogin(String phoneMobile, String md5Pwd) {
//        //查询用户信息
//        QueryWrapper wrapper = new QueryWrapper();
//        wrapper.select(SYS_USER.IS_DISABLE, SYS_USER.PASSWORD, SYS_USER.ACCOUNT, SYS_USER.ID, SYS_USER.IS_SUPER);
//        wrapper.from(SysUser.class);
//        wrapper.where(SYS_USER.PHONE_MOBILE.eq(phoneMobile));
//        SysUser userInfo = sysUserMapper.selectOneByQuery(wrapper);
//
//        //注册
//        if (userInfo == null) {
//            String userPwd = PWD.createMd5Pwd(md5Pwd);
//
//            userInfo = new SysUser();
//            userInfo.setAccount("phone" + phoneMobile);
//            userInfo.setPhoneMobile(phoneMobile);
//            userInfo.setPassword(userPwd);
//
//            //插入会议号
//            List<String> hasCodes = mtRoomMapper.selectObjectListByQueryAs(QueryWrapper.create().select(MT_ROOM.MT_CODE), String.class);
//            String randomCode = StringKit.createRandom(10, false, hasCodes);
//            userInfo.setMtCode(randomCode);
//
//            sysUserMapper.insertSelective(userInfo);
//        }
//        //登录
//        else {
//            if ("1".equals(userInfo.getIsDisable())) throw new ServiceException(MSG.USER_IS_DISABLE);
//            String md5Password = MathKit.getMD5(md5Pwd + securityPro.getLoginSalt());
//            if (!userInfo.getPassword().equals(md5Password)) {
//                throw new ServiceException(MSG.USER_LOGIN_PASSWORD_ERROR);
//            }
//        }
//        return getTokenVO(userInfo);
//    }

    @Override
    public boolean userLogout() {
        AuthUser currentUser = AuthKit.getCurrentUser();
        log.error("[用户主动退出] {}", JSONObject.toJSONString(currentUser));
        return true;
    }


    @Override
    public TokenVO refreshToken(String refreshToken) {
        try {
            JwtKit.check(refreshToken);
            return createToken(AuthKit.getCurrentUser(refreshToken));
        }
        //刷新Token过期，强制重新登录
        catch (Exception e) {
            throw new ServiceException(MSG.ER_411);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insert(SysUser param) {
        String nickName = param.getNickName();
        String account = param.getAccount();
        String phoneMobile = param.getPhoneMobile();
        ArrayList<String> roleIdList = StringKit.split(param.getRoleIds());

        if (StringKit.isBlank(nickName) || StringKit.isBlank(account) || StringKit.isBlank(phoneMobile) || roleIdList.size() == 0)
            throw new ServiceException(MSG.PARAM_ER);

        QueryWrapper wrapper = QueryWrapper.create().where(SYS_USER.ACCOUNT.eq(account));
        SysUser queryUser = sysUserMapper.selectOneByQuery(wrapper);
        if (queryUser != null) throw new ServiceException(MSG.ACCOUNT_EXISTS);

        wrapper = QueryWrapper.create().where(SYS_USER.PHONE_MOBILE.eq(phoneMobile));
        queryUser = sysUserMapper.selectOneByQuery(wrapper);
        if (queryUser != null) throw new ServiceException(MSG.PHONE_MOBILE_EXISTS);

        //插入用户信息
        SysUser sysUser = new SysUser();
        sysUser.setId(IdKit.getId());
        sysUser.setAccount(account);
        sysUser.setNickName(nickName);
        sysUser.setPhoneMobile(phoneMobile);
        sysUserMapper.insertSelective(sysUser);

        //插入用户角色信息
        insertRoleUser(sysUser.getId(), roleIdList);

        return true;
    }


    @Override
    public boolean delete(String ids) {

        ArrayList<String> split = StringKit.split(ids);

        int i = sysUserMapper.deleteBatchByIds(split);

        return true;
    }

    @Override
    public SysUser getUserInfoByAccount(String account) {
        return null;
    }

    @Override
    public SysUser getUserInfoByPhoneMobile(String phoneMobile) {
        return null;
    }

    @Override
    public SysUser appUserInfo() {
        String userId = AuthKit.mastLogin().getUserId();
        SysUser sysUser = sysUserMapper.selectOneById(userId);
        if (sysUser != null) {
            sysUser.setPassword("");
        }
        return sysUser;
    }

    @Override
    public SysUser systemUserInfo() {
        String userId = AuthKit.mastLogin().getUserId();
        SysUser sysUser = sysUserMapper.selectOneById(userId);
        if (sysUser != null) {
            sysUser.setPassword("");
            setUserRole(sysUser);
        }
        return sysUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(SysUser param) {
        String id = param.getId();
        String nickName = param.getNickName();
        String phoneMobile = param.getPhoneMobile();
        ArrayList<String> roleIdList = StringKit.split(param.getRoleIds());

        if (StringKit.isBlank(id) || StringKit.isBlank(nickName) || StringKit.isBlank(phoneMobile) || roleIdList.size() == 0)
            throw new ServiceException(MSG.PARAM_ER);

        QueryWrapper wrapper = QueryWrapper.create().from(SYS_USER).where(SYS_USER.PHONE_MOBILE.eq(phoneMobile)).and(SYS_USER.ID.ne(id));
        if (sysUserMapper.selectOneByQuery(wrapper) != null) throw new ServiceException(MSG.PHONE_MOBILE_EXISTS);

        //更新用户信息
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setNickName(nickName);
        sysUser.setPhoneMobile(phoneMobile);
        sysUser.setIsDisable(param.getIsDisable());
        sysUserMapper.update(sysUser);

        //删除用户角色
        userRoleMapper.deleteByQuery(QueryWrapper.create().where("user_id = ?", id));

        //插入用户角色
        insertRoleUser(id, roleIdList);

        return true;
    }

    private void insertRoleUser(String userId, List<String> roleIdList) {
        ArrayList<LkUserRole> userRoles = new ArrayList<>();
        for (String roleId : roleIdList) {
            LkUserRole lkUserRole = new LkUserRole();
            lkUserRole.setUserId(userId);
            lkUserRole.setRoleId(roleId);
            userRoles.add(lkUserRole);
        }
        userRoleMapper.insertBatch(userRoles);
    }

    @Override
    public Page<SysUser> page(SysUser param, PageSearch pageSearch) {
        QueryWrapper wrapper =
                QueryWrapper.create().select("*")
                        .from(SysUser.class)
                        .where(SYS_USER.ACCOUNT.like(param.getAccount()))
                        .and(SYS_USER.NICK_NAME.like(param.getNickName()))
                        .and(SYS_USER.PHONE_MOBILE.like(param.getPhoneMobile()))
                        .and(SYS_USER.IS_DISABLE.eq(param.getIsDisable()))
                        .and(SYS_USER.ID_CARD_NAME.like(param.getIdCardName()))
                        .and(SYS_USER.ID_CARD_NUMBER.like(param.getIdCardNumber()))
                        .orderBy(SYS_USER.CT.desc());

        Page<SysUser> page = sysUserMapper.paginate(pageSearch.getPageNumber(), pageSearch.getPageSize(), wrapper);
        List<SysUser> records = page.getRecords();
        for (SysUser sysUser : records) {
            sysUser.setPassword("");
            setUserRole(sysUser);
        }
        return page;
    }

    private void setUserRole(SysUser sysUser) {
        List<String> roleIds = userRoleMapper.selectObjectListByQueryAs(QueryWrapper.create().select("role_id").where("user_id = ?", sysUser.getId()), String.class);
        sysUser.setRoleIds(StringKit.join(roleIds, ","));
        if (roleIds.size() > 0) {
            List<SysRole> sysRoles = sysRoleMapper.selectListByIds(roleIds);
            List<String> names = sysRoles.stream().map(SysRole::getName).collect(Collectors.toList());
            sysUser.setRoleNames(StringKit.join(names, ","));
        }
    }

    @Override
    public boolean resetUserPassword(String userIds, String newPassword) {
        ArrayList<String> userIdList = StringKit.split(userIds);

        for (String userId : userIdList) {
            SysUser sysUser = new SysUser();
            sysUser.setId(userId);
            sysUser.setPassword(PWD.createPlainPwd(newPassword));
            sysUserMapper.update(sysUser);
        }

        return true;
    }

    @Override
    public boolean resetMyPassword(String oldPassword, String newPassword) {
        AuthUser authUser = AuthKit.mastLogin();
        String userId = authUser.getUserId();

        //查询用户信息,比对旧密码
        QueryWrapper wrapper = new QueryWrapper().select(SYS_USER.PASSWORD).from(SYS_USER).where(SYS_USER.ID.eq(userId));
        SysUser userInfo = sysUserMapper.selectOneByQuery(wrapper);
        if (!PWD.matchPlainPwd(oldPassword, userInfo.getPassword())) {
            throw new ServiceException(MSG.USER_OLD_PASSWORD_ERROR);
        }

        SysUser sysUser = new SysUser();
        sysUser.setId(userId);
        sysUser.setPassword(PWD.createPlainPwd(newPassword));
        sysUserMapper.update(sysUser);

        return true;
    }


    private TokenVO createToken(SysUser userInfo) {
        QueryWrapper wrapper = new QueryWrapper();
        AuthUser authUser = new AuthUser();
        authUser.setAccount(userInfo.getAccount());
        authUser.setUserId(userInfo.getId());

        wrapper.select("role_id").from("lk_user_role").where("user_id =?", authUser.getUserId());

        List<String> objects = sysUserMapper.selectObjectListByQueryAs(wrapper, String.class);

        authUser.setRoleIds(objects);

        authUser.setRoot(userInfo.getAccount().equals("root"));

        return createToken(authUser);
    }

    private TokenVO createToken(AuthUser authUser) {
        //生成Token
        long systemAccessTokenExpireSecond = dictService.getLong("system_access_token_expire_second", 30 * 24 * 60 * 60);
        //int systemRefreshTokenExpireMinute = dictService.getInt("system_refresh_token_expire_second", 180 * 24 * 60);

        //身份Token30天失效一次
        String accessToken = JwtKit.createToken(JSONObject.toJSONString(authUser), systemAccessTokenExpireSecond);
        //刷新Token不失效
        String refreshToken = JwtKit.createToken(JSONObject.toJSONString(authUser));

        TokenVO tokenVO = new TokenVO(accessToken, refreshToken);

        //记录ID用于统计24小时内登录数量
        cache.set(CacheKey.USER_LOGIN_COUNT_24_HOUR, authUser.getUserId(), 24 * 60L);

        return tokenVO;
    }

    @Override
    public SysUser updateUserInfo(SysUser param) {
        sysUserMapper.update(param);
        return appUserInfo();
    }
}
